#ifdef COMPILE_OPENGL

#include "CBL_Debug.h"
#include "COGL_Context.h"
#include "CRL_ContextFactory.h"
#include "CRL_BufferAtomic.h"

#include <OpenGL/OpenGL.h>
#include <OpenGL/glext.h>

CB_SUBNAMESPACE_START(OpenGL)

CContext::CContext()
{
	GLint MaxAttachmentUnits;
	GLint MaxPixelBufferUnits;
	
	GLint MaxVtxFloatConstants;
	GLint MaxVtxIntConstants;
	GLint MaxVtxBoolConstants;
	
	GLint MaxFrgFloatConstants;
	GLint MaxFrgIntConstants;
	GLint MaxFrgBoolConstants;
	
	glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &MaxAttachmentUnits);
	glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &MaxPixelBufferUnits);
	
	glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &MaxVtxFloatConstants);
	glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &MaxVtxIntConstants);
	glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &MaxVtxBoolConstants);
	
	glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &MaxFrgFloatConstants);
	glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &MaxFrgIntConstants);
	glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &MaxFrgBoolConstants);
	
	m_MaxColorTargets = MaxAttachmentUnits;
	m_MaxPixelBufferUnits = MaxPixelBufferUnits;
	
	// todo: card dependent value changes
	
	m_pColorTargets          = new RenderLib::CBufferAtomicPtr[MaxAttachmentUnits];
	
	m_VertexShaderConstants.m_NumRegisters[RenderLib::IContext::FloatRegister] = MaxVtxFloatConstants;
	m_VertexShaderConstants.m_NumRegisters[RenderLib::IContext::IntRegister]   = MaxVtxIntConstants;
	m_VertexShaderConstants.m_NumRegisters[RenderLib::IContext::BoolRegister]  = MaxVtxBoolConstants;
	
	m_VertexShaderConstants.m_FloatRegister = new Math::Float4[MaxVtxFloatConstants];
	m_VertexShaderConstants.m_IntRegister   = new Math::Int4[MaxVtxIntConstants];
	m_VertexShaderConstants.m_BoolRegister  = new Math::Bool4[MaxVtxBoolConstants];
	
	m_FragmentShaderConstants.m_NumRegisters[RenderLib::IContext::FloatRegister] = MaxFrgFloatConstants;
	m_FragmentShaderConstants.m_NumRegisters[RenderLib::IContext::IntRegister]   = MaxFrgIntConstants;
	m_FragmentShaderConstants.m_NumRegisters[RenderLib::IContext::BoolRegister]  = MaxFrgBoolConstants;
	
	m_FragmentShaderConstants.m_FloatRegister = new Math::Float4[MaxFrgFloatConstants];
	m_FragmentShaderConstants.m_IntRegister   = new Math::Int4[MaxFrgIntConstants];
	m_FragmentShaderConstants.m_BoolRegister  = new Math::Bool4[MaxFrgBoolConstants];	
}

// ---------------------------------------------------------------------------------------------------------------------

CContext::~CContext()
{
	delete[] m_pColorTargets;
	
	delete[] m_VertexShaderConstants.m_FloatRegister;
	delete[] m_VertexShaderConstants.m_IntRegister;
	delete[] m_VertexShaderConstants.m_BoolRegister;
	
	delete[] m_FragmentShaderConstants.m_FloatRegister;
	delete[] m_FragmentShaderConstants.m_IntRegister;
	delete[] m_FragmentShaderConstants.m_BoolRegister;
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::SetVertexConstantBounds(RenderLib::IContext::ERegisterType _Register, uint16 _Start, uint16 _End)
{
	m_VertexShaderConstants.m_RecoveryStartIdx[_Register] = _Start;
	m_VertexShaderConstants.m_RecoveryEndIdx[_Register] = _End;
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::SetFragmentConstantBounds(RenderLib::IContext::ERegisterType _Register, uint16 _Start, uint16 _End)
{
	m_FragmentShaderConstants.m_RecoveryStartIdx[_Register] = _Start;
	m_FragmentShaderConstants.m_RecoveryEndIdx[_Register] = _End;
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::SetColorTarget(uint8 _Stream, RenderLib::CBufferAtomicPtr _PixelBufferAtomic)
{
	m_pColorTargets[_Stream] = _PixelBufferAtomic;
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::SetDepthTarget(RenderLib::CBufferAtomicPtr _PixelBufferAtomic)
{
	m_DepthTarget = _PixelBufferAtomic;
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CBufferAtomicPtr CContext::GetColorTarget(uint8 _Stream)
{
	if (Debug::Verify(_Stream < m_MaxColorTargets))
	{
		return m_pColorTargets[_Stream];
	}
	
	return RenderLib::CBufferAtomicPtr(NULL);
}

// ---------------------------------------------------------------------------------------------------------------------

RenderLib::CBufferAtomicPtr CContext::GetDepthTarget()
{
	return m_DepthTarget;
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::Activate(uint32 _Flags)
{
	RenderLib::CContextFactory& rContextFactory = RenderLib::CContextFactory::GetInstance();
	uint32 KeepColorTargetFlags = _Flags >> 16;
    
    if (((_Flags & FlagKeepColorTarget) == 0) || (KeepColorTargetFlags != 0))
    {
    	// Bind or unbind color targets
        
        for (uint32 ColorTargetIdx=0; 
             (ColorTargetIdx < m_MaxColorTargets) && (ColorTargetIdx < 16);
             ++ColorTargetIdx)
        {
     		if ((KeepColorTargetFlags & (1 << ColorTargetIdx)) == 0)
            {
            	// Try to set the current context's color target.
                // If not set, try to reset the main context's color target
                // If the main context does not have such a target, unbind it
                
            	RenderLib::CBufferAtomicPtr ColorTarget;
            	
                if (m_pColorTargets[ColorTargetIdx].IsValid())
                	ColorTarget = m_pColorTargets[ColorTargetIdx];
                else
                	ColorTarget = rContextFactory.GetMainContext()->GetColorTarget(ColorTargetIdx);
            	
                if (ColorTarget.IsValid())
                {
                	ColorTarget->Bind(ColorTargetIdx);    
                }
                else
                {
                	glFramebufferRenderbufferEXT(GL_RENDERBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + ColorTargetIdx, GL_RENDERBUFFER_EXT, 0);
                }
            }
        }
    }
    
    if ((_Flags & FlagKeepDepthTarget) == 0)
    {
    	// Bind or unbind depth target
        
    	// Try to set the current context's depth target.
    	// If not set, try to reset the main context's depth target
    	// If the main context does not have such a target, unbind it
        
        RenderLib::CBufferAtomicPtr DepthTarget;
    
    	if (m_DepthTarget.IsValid())
            DepthTarget = m_DepthTarget;
        else
        	DepthTarget = rContextFactory.GetMainContext()->GetDepthTarget();
            
    	if (DepthTarget.IsValid())
        {
        	DepthTarget->Bind();    
        }
    	else
        {
        	glFramebufferRenderbufferEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
        }
        
    }
    
    // Handle constant restoring
    
    if ((_Flags & FlagKeepVertexConstants) == 0)
    {
    	if ((_Flags & FlagRestoreVertexConstants) == 0)
        {
        	// TODO: Upload constant block in bounds
        }
        else
        {
         	// TODO: Upload all vertex constants   
        }
    }
    
    if ((_Flags & FlagKeepFragmentConstants) == 0)
    {
    	if ((_Flags & FlagRestoreFragmentConstants) == 0)
        {
        	// TODO: Upload constant block in bounds
        }
    	else
        {
        	// TODO: Upload all vertex constants   
        }   
    }
}

// ---------------------------------------------------------------------------------------------------------------------

void CContext::Render(EPrimitveType _PrimitiveType)
{
}



CB_SUBNAMESPACE_END
#endif